#!/usr/bin/env python
# -*- coding: utf-8 -*-
# python与redis交互：
# 连接方式：
    # 严格连接模式：r=redis.StrictRedis(host="",port=)
    # 更Python化的连接模式：r=redis.Redis(host="",port=)
    # StrictRedis用于实现大部分官方的命令，并使用官方的语法和命令
    # Redis与StrictRedis的区别是：Redis是StrictRedis的子类，用于向前兼容旧版本的redis-py，并且这个连接方式是更加"python化"的
# 连接池：
    # 为了节省资源，减少多次连接损耗，连接池的作用相当于总揽多个客户端与服务端的连接，当新客户端需要连接时，只需要到连接池获取一个连接即可，实际上只是一个连接共享给多个客户端。
# 操作:
    # 值的设置和获取，可以参考redis的命令，redis模块中的对应功能的函数名基本与redis中的一致
    # 【注意默认情况下，设置的值或取得的值都为bytes类型,如果想改为str类型,需要在连接时添加上decode_responses=True】
    # 设置值：
        # redis中set()  ==>r.set()
        # redis中setnx()  ==>r.set()
        # redis中setex() ==>r.setex()
        # redis中setbit()  ==>r.setbit()
        # redis中mset()  == > r.mset()
        # redis中hset()  ==>r.hset()
        # redis中sadd() == >r.sadd()
        # 其他基本redis的命令名与redis模块中的函数名一致
    # 获取:
        # redis中get() ==> r.get()
        # redis中mget() ==> r.mget()
        # redis中getset() ==> r.getset()
        # redis中getrange() ==> r.getrange()
        # 其他基本redis的命令名与redis模块中的函数名一致
import time
import redis

# redis字符串
def testString(r):
    #⽅法set，如果键已经存在则进⾏修改，如果键不存在则进⾏添加
    r.set("string","value")
    value = r.get("string")
    # print(type(value))
    print(value)
    # 设置多个值
    d={
        'user':'Daniel',
        'user1':'Daniel1'
    }
    r.mset(d)
    # 取多个值
    l=['user','user1']
    print(r.mget(l))
    # 删除
    r.delete('user1')

# redis哈希
def testHash(r):
    r.hset("info","name","Daniel")
    r.hset("info","age","14")
    print(r.hget("info","name"))
    print(r.hgetall("info"))
    print(r.hexists('info','name'))

# redis列表
def testList(r):
    r.lpush("list","123")
    r.lpush("list","456")
    print(r.lpop('list'))
    print(r.rpop('list'))
    # 范围取值
    print(r.lrange("list",0, 10))

# redis集合
def testSet(r):
    r.sadd("course","math","english","chinese")
    print(r.smembers("course"))

# redis有序集合
def testZset(r):
    r.zadd("zSet",{"redis":1,"rabbitmq":3,"mysql":0})
    print(r.zrangebyscore("zSet",0,10))
    # 计算元素个数
    print(r.zcard("zSet"))

# 管道：
# 一般情况下，执行一条命令后必须等待结果才能输入下一次命令，管道用于在一次请求中执行多个命令。
# 参数介绍：transaction:指示是否所有的命令应该以原子方式执行。
def testPipeline(r):
    pipe = r.pipeline(transaction=True)
    pipe.set('p1','v1')
    pipe.set('p2','v2')
    pipe.set('p3','v3')
    time.sleep(1)
    pipe.execute()

# # 事务：python中可以使用管道来代替事务：
# # 补充：监视watch：pipe.watch()
def testMulti(r):
    pipe = r.pipeline(transaction=True)
    try:
        pipe.multi()
        pipe.set('Here','There')
        pipe.set('Where','That')
        time.sleep(1)
        pipe.execute()
    except redis.exceptions.WatchError as e:
        print('Error')

# 订阅\发布
# 订阅方：
def testSub(r):
    Flag = True
    chan = r.pubsub()#返回一个发布/订阅对象
    msg_recive = chan.subscribe('cctv0')#订阅
    msg = chan.parse_response()#第一次会返回订阅确认信息
    print(msg)
    print('订阅成功，开始接受.....')
    while Flag:
        msg=chan.parse_response()#接收消息
        print(">>:",msg[2])#此处的信息格式['消息类型', '频道', '消息']，所以使用[2]来获取

# 发布方：
#发布使用publish(self, channel, message):Publish ``message`` on ``channel``.
def testPub(r):
    Flag=True
    while Flag:
        msg=input("主播请讲话>>:")
        if len(msg)==0:
            continue
        elif msg=='quit':
            break
        else:
            r.publish('cctv0',msg)

if __name__=='__main__':
    pool = redis.ConnectionPool(host="127.0.0.1", port=6379,decode_responses=True)
    r = redis.Redis(connection_pool = pool)
    r1 = redis.Redis(connection_pool=pool)
    r.set("name",'Daniel')
    r1.set("Fruits","apple")
    print(r.get('name'))
    print(r.client_list())
    print(r1.client_list())#可以看出两个连接的id是一致的，说明是一个客户端连接

    testString(r)
    testHash(r)
    testList(r)
    testSet(r)
    testZset(r)
    testPipeline(r)
    testMulti(r)
